/**
 * Created by 鄢鹏权 on 2017/04/08.
 */
///<reference path="Config.ts"/>
///<reference path="Request.ts"/>
///<reference path="Common.ts"/>
    ///<reference path="System.ts"/>
    ///<reference path="md5.ts"/>

declare let avalon:any;
declare let require:any;
namespace Avalon{
    /**
     * Avalon的VM类，用于创建VM对象
     */
    export class VM{
        /**
         * avalon的模型名称
         * @type {string}
         */
        $id:string='';
        PageRange=5;
        $NeedLogin=false;
        /**
         * 对象名称
         * @type {string}
         */
        $ObjName:string='';
        /**
         * 模块名称
         * @type {string}
         */
        $ModuleName:string='';
        /**
         * 主键，默认为对象名称+ID
         * @type {string}
         */
        $PK:string='';
        /**
         * 限定返回属性范围，支持数组末位为false时反选
         * @type {boolean}
         */
        Properties:any=false;
        /**
         * 内部模式的html代码内容
         * @type {string}
         */
        html:string='';
        /**
         * 配置是否在搜索时输出报表数据
         * @type {boolean}
         */
        SearchReport=false;
        /**
         * 显示结果
         * @type {{}}
         */
        Data={};
        /**
         * 用于记录数据的索引地址
         * @type {{DataIndex: number; AddDataIndex: number; EditDataIndex: number}}
         */
        index={
            DataIndex:-1
            ,AddDataIndex:-1
            ,EditDataIndex:-1
        };
        /**
         * 添加模式的数据
         * @type {{}}
         */
        AddData={};
        /**
         * 重置添加数据的原始模型
         * @type {{}}
         */
        $AddData={};
        /**
         * 编辑数据的数据模型
         * @type {{}}
         */
        EditData={};
        /**
         * 保存前过滤参数，最后一位为true时表示取反
         * @type {Array}
         */
        $EditDataFields=[];
        /**
         * 添加前过滤参数，最后一位为true时表示取反
         * @type {Array}
         */
        $AddDataFields=[];
        /**
         * 重置编辑数据的数据模型
         * @type {{}}
         */
        $EditData={};
        /**
         *
         * @type {{}}
         */
        $FieldConf={};
        /**
         * 字典表
         * @type {{}}
         */
        DICs:Object={};
        /**
         * 字典数据的配置信息
         * @type {{}}
         */
        $DICsConf={

        };
        /**
         * gets获取的结果集
         * @type {Array}
         */
        List:any[]=[];
        /**
         * 用于search接口的排序规则参数
         * @type {string}
         */
        Sort:string="";
        /**
         * 搜索接口的返回结构，在页面渲染时可以适用for来遍历@Search.L
         * @type Search {{L: Array; P: number; N: number; T: number; Pages: Array}}
         */
        Search:any={
            L:[],P:1,N:10,T:0,Pages:[],TP:0,R:{}
        };
        /**
         * 筛选条件
         */
        Where:{}={};
        /**
         * 模糊搜索的关键词，请适用ms-duplex指令来绑定到input输入框
         * @type {string}
         */
        Keyword:string='';
        /**
         * 默认搜索值
         * @type {{}}
         */
        $Where:{}={};
        /**
         * 报表内容
         * @type {{L: Array; P: number; N: number; T: number; R: {}}}
         */
        Report={
            L:[],P:1,N:10,T:0,R:{},Pages:[]
        };
        /**
         * 模式
         * @type {string}
         */
        Type:string='';
        /**
         * 模型内类型定义，用于弹出模态框的渲染
         * @type {{Detail: string; Add: string; Edit: string}}
         */
        SelfTPL={
            'Detail':'__OBJ__/__OBJ_____TYPE__.html',
            'Add':'__OBJ__/__OBJ_____TYPE__.html',
            'Edit':'__OBJ__/__OBJ___Edit.html',
        };

        /**
         * 构造函数，可以用于直接定义的方式
         * @param {string} id
         * @param {string} ObjName
         * @param {string} ModuleName
         */
        constructor(id:string='',ObjName:string='',ModuleName:string=''){
            this.$id=id;
            this.$ObjName=ObjName.length>0?ObjName:id;
            this.$ModuleName=ModuleName;
            if(!window[this.$ObjName+'VM']){
                window[this.$ObjName+'VM']=this;
            }
        }
        private Inited=false;
        /**
         * 获得输入框时间
         * @param {string} time
         * @returns {any}
         */
        getInputTime=function (time='') {
            let date = time.length>0?new Date(time):new Date();
            return date.format('yyyy-MM-ddThh:mm')
        };
        /**
         * 初始化
         */
        init(){
            if(true===this.Inited){
                return true;
            }
            this.Inited=true;
            let that = this;
            let timeout:any=null;
            let time=0;
            this.$watch('Type',function (n,o) {
                if(that.SelfTPL[n]){
                    let doc=Config.TPLDir+that.SelfTPL[n].replace(/__OBJ__/g,that.$ObjName).replace(/__TYPE__/g,n);
                    if(Config.debug){
                        doc=doc+'?v'+new Date().getTime();
                    }
                    let html = Config.getHTML(that.$ObjName,n,that.$ObjName);
                    if (html.length>0){
                        window[that.$ObjName+'VM'].html=html;
                        that._after_type_change(n,o,html);
                        if(that['ready_'+n] instanceof Function){
                            that['ready_'+n].apply(that,[n])
                        }
                    }else{
                        Castle.Request.getHtml(doc,function (html) {
                            window[that.$ObjName+'VM'].html=html;
                            that._after_type_change(n,o,html);
                            if(that['ready_'+n] instanceof Function){
                                that['ready_'+n].apply(that,[n])
                            }
                        })
                    }
                }else{
                    that._type_change(n,o);
                }
                // if(that['ready_'+n] instanceof Function){
                //     that['ready_'+n].apply(that,[0,{}])
                // }
            });
            if(this.$ObjName==""){
                this.$ObjName=this.$id;
            }
            let reloadSearch=function (n,o) {
                time++;
                if(n==o||time<3)return;
                if(timeout){
                    window.clearTimeout(timeout);
                }
                timeout = window.setTimeout(function () {
                    that.do_search();
                    timeout=null;
                },200)
            };
            this.$watch('Search.P',reloadSearch);
            this.$watch('Search.N',reloadSearch);
            this.$watch('Keyword',reloadSearch);
            // let that = this;
            avalon.each(this.Where,function (i,v) {
                that.$watch('Where.'+i,function (n,o) {
                    if(""===n){
                        if(that.$Where[i]){
                            that.Where[i]=that.$Where[i];
                            // n=that.$Where[i];
                        }
                    }
                    if(that._where_change('Where.'+i,n,o)===true){
                        reloadSearch(n,o)
                    }
                })
            });
            let AddDataKeys = Object.keys(this.AddData);
            let $AddDataKeys = Object.keys(this.$AddData);
            if(AddDataKeys.length>6&&$AddDataKeys.length==0){
                let p = {};
                avalonData.apply(p,[this.AddData])
                this.$AddData=p;
            }
            if(AddDataKeys.length==6&&$AddDataKeys.length>=0){
                this.AddData=this.$AddData;
            }
            let EditDataKeys = Object.keys(this.EditData);
            let $EditDataKeys = Object.keys(this.$EditData);
            if(EditDataKeys.length>6&&$EditDataKeys.length==0){
                let p = {};
                avalonData.apply(p,[this.EditData])
                this.$EditData=p;
            }
            if(EditDataKeys.length==6&&$EditDataKeys.length>=0){
                this.EditData=this.$EditData;
            }
            this.init_DICs();
        }

        /**
         * where数组发生变化时的回掉函数，编写逻辑判断是否应该重新刷新数据
         * @param Field
         * @param n
         * @param o
         * @returns {boolean}
         * @private
         */
        _where_change(Field,n,o){
            return true;
        }

        /**
         * 在vm模式切换后的回掉函数，
         * @param n 新值
         * @param o 旧值
         * @param html
         * @private
         */
        _after_type_change=function (n,o,html) {
            
        };

        /**
         * vm加载就绪
         * @param n
         * @param p
         * @param type
         */
        ready(n:number,p:Object[],type:string){
            this._before_ready(n,p,type);
            if(this.$PK.length<1){this.$PK=this.$ObjName+'ID'}
            this._after_ready(n,p,type);
        }

        /**
         * 加载完成后的回调
         * @param n
         * @param p
         * @param type
         * @private
         */
        _after_ready=function(n:number,p:Object[],type:string){};
        /**
         * 加载前的回调
         * @param n
         * @param p
         * @param type
         * @private
         */
        _before_ready=function(n:number,p:Object[],type:string){};
        /**
         * 重置搜索参数
         */
        reset_search(){
            this.Where=this.$Where;
            this.Keyword='';
        }

        /**
         * 初始字典数据
         */
        init_DICs(){

        }

        /**
         * 重置输入模型，如重置搜索条件则参数为 Where
         * @param param
         */
        reset=function (param){
            if(this[param]&&this['$'+param]){
                this[param]=avalon.mix(this['$'+param])
            }
        };
        /**
         * 发起添加请求，默认为当前vm的AddData
         * @param Data
         * @param success
         * @param error
         * @constructor
         */
        DoAdd=function (Data:any=false,success:any=false,error:any=false) {
            if((Data.clientX>0&&Data.clientY>0) || false===Data){
                Data=this.AddData
            }
            let ad = {};
            avalonData.apply(ad,[Data]);
            // TODO 执行参数校验
            this.do_add(ad,success,this.AddError);
        };
        /**
         * 添加错误的回调函数
         * @param e
         * @param obj
         * @constructor
         */
        AddError=function (e,obj) {

        };
        /**
         * 发起存储请求
         * @param ID 对象主键编号
         * @param Data 对象数据
         * @constructor
         */
        DoSave=function (ID:any=false,Data:any=false) {
            if(Data===false){
                Data=this.EditData
            }
            if((ID.clientX>0&&ID.clientY>0) || false===ID){
                ID=this.EditData[this.$PK];
            }
            if(ID>0){
                let ad = {};
                avalonData.apply(ad,[Data]);
                // TODO 执行参数校验
                this.do_save(ID,ad);
            }else{

            }
        };
        /**
         * 发起删除请求
         * @param ID 对象主键编号
         * @param Index
         * @constructor
         */
        DoDel=function (ID:any=false,Index:any=false) {
            if(Index===false){
                Index=this.EditData
            }
            if((ID.clientX>0&&ID.clientY>0) || false===ID){
                ID=this.EditData[this.$PK];
            }
            this.do_del(ID);
        };
        /**
         * 刷新列表数据，在刷新按钮上使用 :click="@DoRefresh"
         * @constructor
         */
        DoRefresh=function () {
            this.do_search();
        };
        /**
         * List模式就绪时调用
         * @param n
         * @param p
         * @param type
         */
        ready_List=function(n,p,type){
            this.Type='List';
            this.html='';
            this._before_ready_List(n,p,type);
            this.Search.P=n;
            this.do_search();
            this._after_ready_List(n,p,type);
        }
        /**
         * List模式后执行函数
         * @param n
         * @param p
         * @param type
         * @returns {any}
         * @private
         */
        _after_ready_List=function(n,p,type):any{}
        /**
         * List模式前执行函数
         * @param n
         * @param p
         * @param type
         * @returns {any}
         * @private
         */
        _before_ready_List=function(n,p,type):any{}
        /**
         * Detail模式的初始化回调
         * @param n
         * @param p
         * @param type
         */
        ready_Detail=function(n,p,type){
            if(n>0){
                let NeedGet=true;
                let that = this;
                avalon.each(this.Search.L,function (i,v) {
                   if(v[that.$PK]==n){
                       that.Data=v;
                       NeedGet=false;
                       return false;
                   }
                });
                if(NeedGet)
                    this.do_get(n);
            }
            // else if(this.SelfTPL['Detail']==)
            //     history.back();
        }
        /**
         * 返回上一个路由
         */
        back=function () {
            history.back();
        };
        /**
         * add模式的初始化回调
         * @param n
         * @param p
         * @param type
         */
        ready_Add=function(n,p,type){
            this.AddData=clone(this.$AddData);
        };
        /**
         * 编辑状态的初始化回调
         * @param n
         * @param p
         * @param type
         */
        ready_Edit=function(n,p,type){
            if(n>0){
                let NeedGet=true;
                let that = this;
                avalon.each(this.Search.L,function (i,v) {
                    if(v[that.$PK]==n){
                        that.Data=v;
                        NeedGet=false;
                        return false;
                    }
                });
                if(NeedGet)
                    this.do_get(n);
            }
            // else
                // history.back();
        }
        /**
         * 获取一个对象
         * @param ID
         */
        do_get=function(ID:number){
            let req={
                Properties:this.Properties
            }
            req[this.$PK]=ID;
            if(false===this._before_do_get(req)){return;}
            Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,'get',this.$ModuleName),req
                ,function (d,s) {
                this._after_do_get(d);
                this._object_format(d);
                this.Data=d;
            }]);
        }
        _after_do_get=function(d):any{}
        _before_do_get=function(req):any{}
        /**
         * 添加对象
         * @param Obj
         */
        do_add=function(Obj:any,next:string='',success:any=false,error:any=false){
            //TODO 检查判断逻辑，执行参数校验
            if(false===this._before_do_add(Obj)){return;}
            Castle.Request.post.apply(this
                ,[
                    Config.getRequestUrl(this.$ObjName,'add',this.$ModuleName)
                    ,Obj
                    ,function (d,s) {
                        this._object_format(d);
                        this._after_do_add(d);
                        this.Data=d;
                        this.AddData=clone(this.$AddData);
                        if(typeof success == 'function'){
                            success.apply(this,[d,s])
                        }
                        if(next.length>0){
                            this.Type=next;
                        }
                        this.do_search();
                    }
                    ,function (e) {
                        if(typeof error =='function')
                            error.apply(this,[e,Obj])
                    }
                ]);
        }
        _after_do_add=function(d):any{}
        _before_do_add=function(req):any{}
        /**
         * 根据ID删除
         * @param ID
         */
        do_del=function(ID:number,success:any=false,error:any=false){
            let req={}
            req[this.$PK]=ID;
            if(false===this._before_do_del(req)){return;}
            Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,'del',this.$ModuleName),req
                ,function (d,s) {
                    this._after_do_del(d);
                    this.do_search();
                    if(typeof success == 'function'){
                        success.apply(this,[d])
                    }
                }]);
        }
        _after_do_del=function(d):any{}
        _before_do_del=function(req):any{}
        /**
         * 对象格式化回调，该回调会在get/gets/search后被回调，用于格式化本地数据
         * @param obj
         * @private
         */
        _object_format=function(obj):any{};
        _parse_where=function():any{
            let req = {
                Keyword:this.Keyword,
                W:{},
                P:this.Search.P>0?this.Search.P:1,
                N:this.Search.N,
                Sort:this.Sort,
                Properties:this.Properties,
                R:this.SearchReport
            };
            avalonData.apply(req.W,[this.Where]);
            for (let x in req.W){
                let s = /\{[a-zA-Z]+\}/g.exec(req.W[x])
                if(s&&s.length>0){
                    switch (s[0].toUpperCase()){
                        case "{UID}":
                            req.W[x]=index.user.UID;
                            break;
                    }
                }
                if(req.W[x]===""){
                    delete req.W[x]
                }
            }
            return req;
        };
        /**
         * 条件搜索
         */
        do_search=function(){
            let req=this._parse_where();
            if(this.$NeedLogin===true&&index.user.UID.length==0){return;}
            if(false===this._before_do_search(req)){return;}
            Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,'search',this.$ModuleName),req
                ,function (d,s) {
                    if(d.L.length==0&&d.T>0&&d.N>0&&d.P){
                        this.Search.P=1;
                        return;
                    }
                    this._after_do_search(d,req);
                    let that = this;
                    d.L.forEach(function (v,i,a) {
                        that._object_format(v)
                    });
                    d.Pages=[];
                    let TotalPage:any=Number(d.T/d.N);
                    if(TotalPage>Math.floor(TotalPage)){
                        TotalPage+=1;
                    }
                    d.TP=Math.floor(TotalPage);
                    let Start = this.Search.P<3?1:this.Search.P-2;
                    let End = TotalPage>5?Start+5:TotalPage;
                    for (let x = Start;x<=End;x++){
                        d.Pages.push(x);
                    }
                    // if(d.T%d.N>0){d.Pages.push(d.Pages.length+1)}
                    this.Search=d;
                }]);
        }
        /**
         * 发起搜素请求前的回调，可以在该回调中修改req的值已达到过滤搜索参数的效果
         * @param req
         * @returns {any}
         * @private
         */
        _before_do_search=function(req):any{}
        /**
         * 搜索完成后的执行方法，可以用于用户提示
         * @param d
         * @param req
         * @returns {any}
         * @private
         */
        _after_do_search=function(d,req):any{}
        /**
         * 获取指定对象数据
         * @param IDs
         */
        do_gets=function(IDs:number[]){
            let req={
                Properties:this.Properties
            }
            req[this.$PK+'s']=IDs;
            if(false===this._before_do_gets(req)){return;}
            Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,'gets',this.$ModuleName),req
                ,function (d,s) {
                    let that = this;
                    this._after_do_gets(d);
                    d.forEach(function(v,i,a){
                        that._object_format(v)
                    });
                    this.List=d;
                }]);
        }
        /**
         * 发起获取指定对象前的回调
         * @param req
         * @returns {any}
         * @private
         */
        _before_do_gets=function(req):any{}
        _after_do_gets=function(d):any{}
        _type_change=function(n,o){

        };
        /**
         * 调用save接口，拉取Properties定义和Params定义发起保存请求
         * @param {number} ID
         * @param Params
         * @param {string} next
         * @param success
         * @param error
         */
        do_save=function(ID:number,Params:any,next:string='',success:any=false,error:any=false){
            let req={
                Properties:this.Properties,
                Params:Params
            }
            req[this.$PK]=ID;
            if(this.$EditDataFields.length>0){
                let other=this.$EditDataFields[this.$EditDataFields.length-1]===true;
                let keep={};
                this.$EditDataFields.forEach(function (v) {
                    if(other){
                        //删除这些
                        delete req.Params[v]
                    }else{
                        //保留这些
                        keep[v]=req.Params[v]?req.Params[v]:''
                    }
                })
                if(false===other){
                    req.Params=keep
                }
            }
            if(false===this._before_do_save(req)){
                return;
            }
            let that=this;
            Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,'save',this.$ModuleName),req
                ,function (d,s) {
                    if(false===that._after_do_save(req,d)){
                        return;
                    }
                    if(typeof success == 'function'){
                        success.apply(that,[d,s])
                    }
                    if(next.length>0){
                        this.Type=next;
                    }
                }]);
        }
        /**
         * 发起保存数据对象前的回调，可以修改req的对象值实现不可修改部分的禁止提交功能
         * @param req
         * @private
         */
        _before_do_save=function (req) {

        };
        /**
         * 保存后的回调，可以用于判断是否保存成功，执行相关保存成功与否的方法
         * @param req
         * @param d
         * @private
         */
        _after_do_save=function (req,d) {

        };
        /**
         * 报表
         * @param Name
         * @param Params
         * @param Down
         */
        do_report=function(Name:string,Params={},Down=false){
            let req={
                Name:Name,
                Down:Down
            };
            req = avalon.mix(req,Params);
            if(Down){
                Castle.Request.down({
                    url:Config.getRequestUrl(this.$ObjName,'report',this.$ModuleName),
                    data:req
                })
            }else{
                Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,'report',this.$ModuleName),req
                    ,function (d,s) {
                        this.Report=d;
                    }]);
            }
        };

        /**
         * 发起其它请求
         * @param i
         * @param Params
         * @param success
         * @param error
         */
        do_other=function(i:string,Params,success:Function,error:Function){
            Castle.Request.post.apply(this,[Config.getRequestUrl(this.$ObjName,i,this.$ModuleName),Params
                ,success,error]);
        }
        /**
         * 数据请求的条件
         * @type {{}}
         */
        DataWhere={};
        /**
         * 数据报表
         * @type {{L: Array; P: number; N: number; T: number; R: {}; Pages: Array}}
         */
        DataReport={
            L:[],P:1,N:10,T:0,R:{},Pages:[]
        };
        /**
         * 获取报表数据
         * @param Obj 对象名称
         * @param Name 报表名称
         * @param Params 报表参数
         */
        report=function (Obj,Name,Params) {
            Castle.Request.post.apply(this,[Config.getRequestUrl(Obj,'report'),{
                Name:Name,
                Params:Params
            },function (d) {
                this.DataReport=d;
            },function (e) {
                System.warning('获取报表发生错误');
            }]);
        }
    }

    /**
     * 组件类，用于继承后编写avalon组件
     */
    export class Components{
        template:string='';
        defaults={
            /**
             * 组件被加载时触发
             * @param vm
             */
            onInit:function(vm){

            },
            /**
             * 组件就绪时触发
             * @param vm
             */
            onReady:function(vm){

            }
        }
    }
}